#!/usr/bin/python2.4
#
# Copyright 2009 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========================================================================

#
# Hammer file to create the stub .msi and .msp files
#

import os
import stat

Import('env')


def CreateMsiInstallerFiles(env, wxs_file, suffix=''):
  _repair_exe_obj_dir = '$OBJ_ROOT/recovery/repair_exe/'
  _custom_actions_path = (
      _repair_exe_obj_dir + 'custom_action/executecustomaction.dll')
  _cert_file = env.File(GetOption('patching_certificate')).abspath
  _required_file = '$MAIN_DIR/recovery/repair_exe/msp/requiredfile.txt'

  # TODO(omaha): Update the build machine command lines to use
  # patching_certificate instead of authenticode_file and remove this.
  # The following is for backwards compatibility. Historically, the build
  # server, as specified the patching_certificate with --authenticode_file.
  if (env.Bit('build_server') and
      GetOption('patching_certificate') == '$MAIN_DIR/data/OmahaTestCert.cer'):
    _cert_file = env.File(GetOption('authenticode_file')).abspath

  omaha_version_info = env['omaha_versions_info'][0]
  omaha_version_string = omaha_version_info.GetVersionString()

  old_unsigned_env = env.Clone()
  old_unsigned_env.Append(
      WIXCANDLEFLAGS = [
          '-dFinalMsi=0',
          '-dCertificateFile=' + _cert_file,
          '-dRequiredFile=' + env.File(_required_file).abspath,
          '-dGoogleUpdateVersion=' + omaha_version_string,
          ],
      WIXLIGHTFLAGS = [
          '-dRequiredFile=' + env.File(_required_file).abspath,
          '-sval',
          ],
  )

  old_unsigned_output = old_unsigned_env.WiX(
      'GoogleUpdateHelper%s_unsigned.msi' % suffix,
      wxs_file)

  env.Depends(old_unsigned_output, [_custom_actions_path, _required_file])


  new_unsigned_env = env.Clone()
  new_unsigned_env.Append(
      WIXCANDLEFLAGS = [
          '-dFinalMsi=1',
          '-dExecuteCustomActionDLL=' + env.File(_custom_actions_path).abspath,
          '-dCertificateFile=' + _cert_file,
          '-dRequiredFile=' + env.File(_required_file).abspath,
          '-dGoogleUpdateVersion=' + omaha_version_string,
          ],
      WIXLIGHTFLAGS = [
          '-dRequiredFile=' + env.File(_required_file).abspath,
          '-sval',
        ],
  )

  # Output to a subdirectory to avoid build breaks caused by two different
  # actions referencing files with the same name and path.
  new_unsigned_env['WIXOBJPREFIX'] = new_unsigned_env['WIXOBJPREFIX'] + 'new/'

  new_unsigned_output = new_unsigned_env.WiX(
      target='new/GoogleUpdateHelper%s_unsigned.msi' % suffix,
      source=wxs_file
  )

  env.Depends(new_unsigned_output, [_custom_actions_path, _required_file])
  return (old_unsigned_output, new_unsigned_output)


#
# Create the MSP file
#
def CreateMsiPatchFile(env, wxs_patch_file, old_msi, new_msi, suffix=''):
  msp_env = env.Clone()

  patch_output = msp_env.Command(
      target='patch%s.wixobj' % suffix,
      source=wxs_patch_file,
      action=('@candle.exe -nologo -out $TARGET $SOURCE -dAfterImage=%s'
              ' -dBeforeImage=%s' % (env.File(new_msi[0]).abspath,
                                     env.File(old_msi[0]).abspath))
  )

  # Required because the before and after images are not in the source.
  Depends(patch_output, [old_msi, new_msi])

  pcp_output = msp_env.Command(
      target='patch%s.pcp' % suffix,
      source=patch_output,
      action='@light.exe -nologo -out $TARGET $SOURCE'
  )

  # The PCP, and thus the MSP, fail to rebuild when the MSI files change without
  # this explicit dependency, probably because the .wixobj hash does not change.
  Depends(pcp_output, [patch_output, old_msi, new_msi])

  # Delete temp dir that vista sdk version of msimsp.exe cannot remove for
  # itself.
  _temp_dir = os.path.join(env['ENV']['TMP'], '~pcw_tmp.tmp')
  if os.path.exists(_temp_dir):
    # Recursively delete subdirectories
    def rm_rf(dir):
      for file in os.listdir(dir):
        path = os.path.join(dir, file)
        if os.path.isdir(path):
          rm_rf(path)
        else:
          os.chmod(path, stat.S_IWRITE)  # Make sure file is writeable.
          os.remove(path)
      os.rmdir(dir)

    # Remove the temp dir.
    rm_rf(_temp_dir)

  unsigned_msp_name = 'GoogleUpdateHelperPatch%s_unsigned.msp' % suffix
  unsigned_msp_path = '$OBJ_ROOT/recovery/repair_exe/msp/' + unsigned_msp_name

  msp_output = msp_env.Command(
      target=unsigned_msp_name,
      source=pcp_output,
      action='@msimsp.exe -s $SOURCE -p $TARGET -l %s' % (
          env.File(unsigned_msp_path + '.log').abspath),
  )

  # For unknown reasons, the PCP fails to rebuild and cause the MSP to rebuild
  # when the MSI files change without this explicit dependency.
  Depends(msp_output, pcp_output)
  return msp_output


old_msi, new_msi = CreateMsiInstallerFiles(
    env, 'patchableinstaller.wxs')
old_msi_for_legacy, new_msi_for_legacy = CreateMsiInstallerFiles(
    env, 'legacy/patchableinstaller.wxs', 'legacy')

msp_output = CreateMsiPatchFile(env, 'patch.wxs', old_msi, new_msi)
msp_output_for_legacy = CreateMsiPatchFile(
    env,
    'legacy/patch.wxs',
    old_msi_for_legacy,
    new_msi_for_legacy,
    'legacy')

#
# Sign the old MSI and MSP files
#
signed_msi = env.SignedBinary(
    target='GoogleUpdateHelper.msi',
    source=old_msi,
)

signed_msp = env.SignedBinary(
    target='GoogleUpdateHelperPatch.msp',
    source=msp_output,
)

signed_msp_for_legacy = env.SignedBinary(
    target='GoogleUpdateHelperPatchWithLegacyID.msp',
    source=msp_output_for_legacy,
)

env.Replicate('$STAGING_DIR', [signed_msi, signed_msp, signed_msp_for_legacy])
